package com.train.base.service.impl;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.SnowflakeIdWorker;
import com.ruoyi.common.utils.StringUtils;
import com.train.base.converter.QuestionConverter;
import com.train.base.domain.ExamQuestionBank;
import com.train.base.domain.ExamQuestionBankOption;
import com.train.base.domain.ExamSubjectKnowledgePoints;
import com.train.base.domain.ExamTestPaperExtractRule;
import com.train.base.entity.excel.*;
import com.train.base.mapper.ExamQuestionBankMapper;
import com.train.base.service.IExamQuestionBankService;
import com.train.base.service.IExamSubjectKnowledgePointsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 题库管理Service业务层处理
 *
 * @author ruoyi
 * @date 2024-03-26
 */
@Service
public class ExamQuestionBankServiceImpl implements IExamQuestionBankService {
    @Autowired
    private ExamQuestionBankMapper examQuestionBankMapper;
    @Autowired
    private QuestionConverter questionConverter;
    @Autowired
    private IExamSubjectKnowledgePointsService examSubjectKnowledgePointsService;

    /**
     * 查询题库管理
     *
     * @param id 题库管理主键
     * @return 题库管理
     */
    @Override
    public ExamQuestionBank selectExamQuestionBankById(Long id) {
        return examQuestionBankMapper.selectExamQuestionBankById(id);
    }

    /**
     * 查询题库管理列表
     *
     * @param examQuestionBank 题库管理
     * @return 题库管理
     */
    @Override
    public List<ExamQuestionBank> selectExamQuestionBankList(ExamQuestionBank examQuestionBank) {
        List<ExamQuestionBank> examQuestionBanks = examQuestionBankMapper.query(examQuestionBank);
        Set<Long> examSubjectKnowledgePointsIds = new HashSet<>();
        examQuestionBanks.forEach(e -> {
            examSubjectKnowledgePointsIds.add(e.getKnowledgePointsId());
            examSubjectKnowledgePointsIds.add(e.getSubjectId());
        });
        if (!examSubjectKnowledgePointsIds.isEmpty()) {
            List<ExamSubjectKnowledgePoints> examSubjectKnowledgePoints = examSubjectKnowledgePointsService.selectExamSubjectKnowledgePointsByIds(examSubjectKnowledgePointsIds);
            Map<Long, String> map = examSubjectKnowledgePoints.stream().collect(Collectors.toMap(ExamSubjectKnowledgePoints::getId, ExamSubjectKnowledgePoints::getName));
            examQuestionBanks.forEach(e -> {
                e.setSubjectName(map.get(e.getSubjectId()));
                e.setKnowledgePointsName(map.get(e.getKnowledgePointsId()));
            });
        }
        return examQuestionBanks;
    }

    /**
     * 新增题库管理
     *
     * @param examQuestionBank 题库管理
     * @return 结果
     */
    @Transactional
    @Override
    public int insertExamQuestionBank(ExamQuestionBank examQuestionBank) {
        examQuestionBank.setCreateTime(LocalDateTime.now());
        examQuestionBank.setId(SnowflakeIdWorker.build().nextId());
        examQuestionBank.setIsDel(0L);
        int rows = examQuestionBankMapper.insertExamQuestionBank(examQuestionBank);
        insertExamQuestionBankOption(examQuestionBank);
        return rows;
    }

    /**
     * 修改题库管理
     *
     * @param examQuestionBank 题库管理
     * @return 结果
     */
    @Transactional
    @Override
    public int updateExamQuestionBank(ExamQuestionBank examQuestionBank) {
        examQuestionBank.setUpdateTime(DateUtils.getNowDate());
        examQuestionBankMapper.deleteExamQuestionBankOptionByQuestionId(examQuestionBank.getId());
        insertExamQuestionBankOption(examQuestionBank);
        return examQuestionBankMapper.updateExamQuestionBank(examQuestionBank);
    }

    /**
     * 批量删除题库管理
     *
     * @param ids 需要删除的题库管理主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteExamQuestionBankByIds(Long[] ids) {
        examQuestionBankMapper.deleteExamQuestionBankOptionByQuestionIds(ids);
        return examQuestionBankMapper.deleteExamQuestionBankByIds(ids);
    }

    /**
     * 删除题库管理信息
     *
     * @param id 题库管理主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteExamQuestionBankById(Long id) {
        examQuestionBankMapper.deleteExamQuestionBankOptionByQuestionId(id);
        return examQuestionBankMapper.deleteExamQuestionBankById(id);
    }

    @Override
    public ReadListener<Map<Integer, String>> getJudgeQuestionReadListener() {
        return new AnalysisEventListener<>() {
            Map<Integer, String> head = new HashMap<>();
            List<JudgeQuestionExcel> cachedDataList = new ArrayList<>();

            @Override
            public void invoke(Map<Integer, String> data, AnalysisContext context) {
                try {
                    BaseQuestionExcel baseQuestionExcel = mapToExcel(data, head);
                    cachedDataList.add(questionConverter.judgeQuestionExcelConverter(baseQuestionExcel));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                headMap.forEach((k, v) -> {
                    head.put(k, v.getStringValue());
                });
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                try {
                    List<ExamQuestionBank> examQuestionBanks = questionConverter.judgeQuestionExcelsToPos(cachedDataList);
                    examQuestionBankMapper.batchInsertExamQuestionBank(examQuestionBanks);
                } catch (Exception e) {
                    throw new RuntimeException("判断题导入存在问题：" + e.getMessage());
                }
            }
        };
    }

    @Override
    public ReadListener<Map<Integer, String>> getEssayQuestionReadListener() {
        return new AnalysisEventListener<>() {
            Map<Integer, String> head = new HashMap<>();
            List<EssayQuestionExcel> cachedDataList = new ArrayList<>();

            @Override
            public void invoke(Map<Integer, String> data, AnalysisContext context) {
                try {
                    BaseQuestionExcel baseQuestionExcel = mapToExcel(data, head);
                    cachedDataList.add(questionConverter.essayQuestionExcelConverter(baseQuestionExcel));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                headMap.forEach((k, v) -> {
                    head.put(k, v.getStringValue());
                });
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                try{
                    List<ExamQuestionBank> examQuestionBanks = questionConverter.essayQuestionExcelsToPos(cachedDataList);
                    examQuestionBankMapper.batchInsertExamQuestionBank(examQuestionBanks);
                }catch (Exception e){
                    throw new RuntimeException("论述题导入存在问题：" + e.getMessage());
                }
            }
        };
    }

    @Override
    public ReadListener<Map<Integer, String>> getChoiceQuestionReadListener() {
        return new AnalysisEventListener<>() {
            Map<Integer, String> head = new HashMap<>();
            List<ChoiceQuestionExcel> cachedDataList = new ArrayList<>();

            @Override
            public void invoke(Map<Integer, String> data, AnalysisContext context) {
                try {
                    BaseQuestionExcel baseQuestionExcel = mapToExcel(data, head);
                    cachedDataList.add(questionConverter.choiceQuestionExcelConverter(baseQuestionExcel, data, head));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                headMap.forEach((k, v) -> {
                    head.put(k, v.getStringValue());
                });
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                try {
                    List<ExamQuestionBank> examQuestionBanks = questionConverter.choiceQuestionExcelsToPos(cachedDataList);
                    examQuestionBankMapper.batchInsertExamQuestionBank(examQuestionBanks);
                    List<ExamQuestionBankOption> options = new ArrayList<>();
                    cachedDataList.forEach(e -> {
                        for (int i = 0; i < e.getAlternativeAnswer().size(); i++) {
                            String content = e.getAlternativeAnswer().get(i);
                            if (StringUtils.isEmpty(content)) {
                                continue;
                            }
                            ExamQuestionBankOption option = new ExamQuestionBankOption();
                            option.setId(SnowflakeIdWorker.build().nextId());
                            option.setQuestionId(e.getId());
                            option.setCreateBy(SecurityUtils.getUsername());
                            option.setContent(content);
                            option.setSort(Long.valueOf(i));
                            option.setIsDel(0L);
                            options.add(option);
                        }
                    });
                    examQuestionBankMapper.batchExamQuestionBankOption(options);
                }catch (Exception e){
                    throw new RuntimeException("选择题导入存在问题：" + e.getMessage());
                }
            }
        };
    }

    @Override
    public ReadListener<Map<Integer, String>> getFillBlankQuestionReadListener() {
        return new AnalysisEventListener<>() {
            Map<Integer, String> head = new HashMap<>();
            List<FillBlankQuestionExcel> cachedDataList = new ArrayList<>();

            @Override
            public void invoke(Map<Integer, String> data, AnalysisContext context) {
                try {
                    BaseQuestionExcel baseQuestionExcel = mapToExcel(data, head);
                    cachedDataList.add(questionConverter.fillBlankQuestionExcelConverter(baseQuestionExcel, data, head));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                headMap.forEach((k, v) -> {
                    head.put(k, v.getStringValue());
                });
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                try{
                    List<ExamQuestionBank> examQuestionBanks = questionConverter.fillBlankQuestionExcelsToPos(cachedDataList);
                    examQuestionBankMapper.batchInsertExamQuestionBank(examQuestionBanks);
                    List<ExamQuestionBankOption> options = new ArrayList<>();
                    cachedDataList.forEach(e -> {
                        for (int i = 0; i < e.getAnswer().size(); i++) {
                            if(Objects.isNull(e.getAnswer().get(i))){
                                continue;
                            }
                            ExamQuestionBankOption option = new ExamQuestionBankOption();
                            option.setId(SnowflakeIdWorker.build().nextId());
                            option.setQuestionId(e.getId());
                            option.setCreateBy(SecurityUtils.getUsername());
                            option.setContent(e.getAnswer().get(i));
                            option.setSort(Long.valueOf(i));
                            option.setIsDel(0L);
                            options.add(option);
                        }
                    });
                    examQuestionBankMapper.batchExamQuestionBankOption(options);
                }catch (Exception e){
                    throw new RuntimeException("填空题导入存在问题：" + e.getMessage());
                }
            }
        };
    }

    @Override
    public List<ExamQuestionBank> selecRandByRule(ExamTestPaperExtractRule rule) {
        return examQuestionBankMapper.selectRandByRule(rule);
    }

    @Override
    public List<ExamQuestionBank> selectExamQuestionBankByIds(List<Long> bankIds) {
        if (bankIds.isEmpty()) {
            return Collections.emptyList();
        }
        List<ExamQuestionBank> examQuestionBanks = examQuestionBankMapper.selectExamQuestionBankByIds(bankIds);
        Set<Long> examSubjectKnowledgePointsIds = new HashSet<>();
        examQuestionBanks.forEach(e -> {
            examSubjectKnowledgePointsIds.add(e.getKnowledgePointsId());
            examSubjectKnowledgePointsIds.add(e.getSubjectId());
        });
        if (!examSubjectKnowledgePointsIds.isEmpty()) {
            List<ExamSubjectKnowledgePoints> examSubjectKnowledgePoints = examSubjectKnowledgePointsService.selectExamSubjectKnowledgePointsByIds(examSubjectKnowledgePointsIds);
            Map<Long, String> map = examSubjectKnowledgePoints.stream().collect(Collectors.toMap(ExamSubjectKnowledgePoints::getId, ExamSubjectKnowledgePoints::getName));
            examQuestionBanks.forEach(e -> {
                e.setSubjectName(map.get(e.getSubjectId()));
                e.setKnowledgePointsName(map.get(e.getKnowledgePointsId()));
            });
        }
        return examQuestionBanks;
    }

    @Override
    public List<ExamQuestionBank> listByPost(Long postId, String postLevelCode) {
        return examQuestionBankMapper.listByPost(postId, postLevelCode);
    }

    @Override
    public List<ExamQuestionBankOption> listOptionsByBankId(Long id) {
        return examQuestionBankMapper.listOptionsByBankId(id);
    }

    /**
     * 新增考题选项 适用于填空、选择题信息
     *
     * @param examQuestionBank 题库管理对象
     */
    public void insertExamQuestionBankOption(ExamQuestionBank examQuestionBank) {
        List<ExamQuestionBankOption> examQuestionBankOptionList = examQuestionBank.getExamQuestionBankOptionList();
        Long id = examQuestionBank.getId();
        if (StringUtils.isNotNull(examQuestionBankOptionList)) {
            List<ExamQuestionBankOption> list = new ArrayList<ExamQuestionBankOption>();
            for (ExamQuestionBankOption examQuestionBankOption : examQuestionBankOptionList) {
                examQuestionBankOption.setId(SnowflakeIdWorker.build().nextId());
                examQuestionBankOption.setQuestionId(id);
                examQuestionBankOption.setIsDel(0L);
                list.add(examQuestionBankOption);
            }
            if (list.size() > 0) {
                examQuestionBankMapper.batchExamQuestionBankOption(list);
            }
        }
    }

    private List<Field> declaredFields;

    private BaseQuestionExcel mapToExcel(Map<Integer, String> data, Map<Integer, String> head) throws IllegalAccessException {
        BaseQuestionExcel baseQuestionExcel = new BaseQuestionExcel();
        Class<BaseQuestionExcel> cls = BaseQuestionExcel.class;
        if (CollectionUtils.isEmpty(declaredFields)) {
            declaredFields = Arrays.stream(cls.getDeclaredFields()).toList();
        }
        for (Field field : declaredFields) {
            Annotation[] fieldAnnotations = field.getDeclaredAnnotations();
            for (Annotation annotation : fieldAnnotations) {
                if (annotation.annotationType() == ExcelProperty.class) {
                    ExcelProperty excelProperty = (ExcelProperty) annotation;
                    field.setAccessible(true);
                    for (Map.Entry<Integer, String> entry : head.entrySet()) {
                        if (Arrays.stream(excelProperty.value()).toList().contains(entry.getValue())) {
                            field.set(baseQuestionExcel, data.get(entry.getKey()));
                        }
                    }
                }
            }
        }
        return baseQuestionExcel;
    }
}
